package net.koodar.forge.security.config;

import cn.hutool.core.util.StrUtil;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import net.koodar.forge.security.domain.dto.RequestUrlDTO;
import net.koodar.forge.security.annoation.NoNeedLogin;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author liyc
 */
@Configuration
@Slf4j
public class UrlConfig {

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    /**
     * 获取每个方法的请求路径
     */
    @Bean
    public Map<Method, Set<String>> methodUrlMap() {
        Map<Method, Set<String>> methodUrlMap = new HashMap<>();
        //获取url与类和方法的对应信息
        Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
            RequestMappingInfo requestMappingInfo = entry.getKey();

            Set<PathPattern> urlPaths = requestMappingInfo.getPathPatternsCondition().getPatterns();
            if (CollectionUtils.isEmpty(urlPaths)) {
                continue;
            }

            Set<String> urls = urlPaths.stream().map(PathPattern::getPatternString).collect(Collectors.toSet());
            HandlerMethod handlerMethod = entry.getValue();
            methodUrlMap.put(handlerMethod.getMethod(), urls);
        }
        return methodUrlMap;
    }

    /**
     * 获取无需登录可以匿名访问的url信息
     */
    @Bean
    public List<String> noNeedLoginUrlList(Map<Method, Set<String>> methodUrlMap) {
        List<String> noNeedLoginUrlList = new ArrayList<>();
        for (Map.Entry<Method, Set<String>> entry : methodUrlMap.entrySet()) {
            Method method = entry.getKey();
            NoNeedLogin noNeedLogin = method.getAnnotation(NoNeedLogin.class);
            if (null == noNeedLogin) {
                continue;
            }
            noNeedLoginUrlList.addAll(entry.getValue());
        }
        log.info("不需要登录的URL：{}", noNeedLoginUrlList);
        return noNeedLoginUrlList;
    }

    /**
     * 获取忽略的url信息
     */
    @Bean
    public List<String> ignoreUrlList() {
        List<String> ignoreUrlList = new ArrayList<>();
        ignoreUrlList.add("/swagger-ui.html");
        ignoreUrlList.add("/swagger-ui/**");
        ignoreUrlList.add("/v3/api-docs/**");
        return ignoreUrlList;
    }

    /**
     * 需要进行url权限校验的方法
     */
    @Bean
    public List<RequestUrlDTO> authUrl(Map<Method, Set<String>> methodUrlMap) {
        List<RequestUrlDTO> authUrlList = new ArrayList<>();
        for (Map.Entry<Method, Set<String>> entry : methodUrlMap.entrySet()) {
            Method method = entry.getKey();
            PreAuthorize preAuthorize = method.getAnnotation(PreAuthorize.class);
            if (null == preAuthorize) {
                continue;
            }
            List<RequestUrlDTO> requestUrlList = this.buildRequestUrl(method, entry.getValue());
            authUrlList.addAll(requestUrlList);
        }
        log.info("需要权限校验的URL：{}", authUrlList.stream().map(RequestUrlDTO::getUrl).collect(Collectors.toList()));
        return authUrlList;
    }

    private List<RequestUrlDTO> buildRequestUrl(Method method, Set<String> urlSet) {
        List<RequestUrlDTO> requestUrlList = new ArrayList<>();
        if (CollectionUtils.isEmpty(urlSet)) {
            return requestUrlList;
        }
        //url对应的方法名称
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        List<String> list = StrUtil.split(className, ".");
        String controllerName = list.get(list.size() - 1);
        String name = controllerName + "." + methodName;
        //swagger 说明信息
        String methodComment = null;
        Operation apiOperation = method.getAnnotation(Operation.class);
        if (apiOperation != null) {
            methodComment = apiOperation.summary();
        }
        for (String url : urlSet) {
            RequestUrlDTO requestUrlVO = new RequestUrlDTO();
            requestUrlVO.setUrl(url);
            requestUrlVO.setName(name);
            requestUrlVO.setComment(methodComment);
            requestUrlList.add(requestUrlVO);
        }
        return requestUrlList;
    }

}
